
#include "hub75e.h"
#include <string.h>

// 8x16 ASCII font (only for A-Z, 0-9, space, colon, dash)
static const uint8_t font8x16[][16] = {
    // Only a minimal set: space (0), '-', ':', '0'-'9', 'A'-'Z'
    // Space (32)
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    // '-'
    {0,0,0,0,0,0,0,0x7E,0x7E,0,0,0,0,0,0,0},
    // ':'
    {0,0,0,0,0x18,0x3C,0x3C,0x18,0,0x18,0x3C,0x3C,0x18,0,0,0},
    // '0'
    {0x3C,0x7E,0xE7,0xCF,0xDF,0xF7,0xE7,0x7E,0x3C,0,0,0,0,0,0,0},
    // '1'
    {0x18,0x38,0x78,0x18,0x18,0x18,0x18,0x18,0x7E,0x7E,0,0,0,0,0,0},
    // '2'
    {0x3C,0x7E,0xE7,0x07,0x0E,0x1C,0x38,0x70,0xE0,0xFF,0xFF,0,0,0,0,0},
    // '3'
    {0x3C,0x7E,0xE7,0x07,0x1E,0x1E,0x07,0xE7,0x7E,0x3C,0,0,0,0,0,0},
    // '4'
    {0x0E,0x1E,0x3E,0x6E,0xCE,0xFF,0xFF,0x0E,0x0E,0x0E,0,0,0,0,0,0},
    // '5'
    {0xFF,0xFF,0xE0,0xFC,0xFE,0x07,0x07,0xE7,0x7E,0x3C,0,0,0,0,0,0},
    // '6'
    {0x3C,0x7E,0xE7,0xE0,0xFC,0xFE,0xE7,0xE7,0x7E,0x3C,0,0,0,0,0,0},
    // '7'
    {0xFF,0xFF,0x07,0x0E,0x1C,0x38,0x70,0xE0,0xE0,0xE0,0,0,0,0,0,0},
    // '8'
    {0x3C,0x7E,0xE7,0xE7,0x7E,0x3C,0x7E,0xE7,0xE7,0x7E,0x3C,0,0,0,0,0},
    // '9'
    {0x3C,0x7E,0xE7,0xE7,0x7F,0x3F,0x07,0xE7,0x7E,0x3C,0,0,0,0,0,0},
    // 'A'
    {0x3C,0x7E,0xE7,0xE7,0xE7,0xFF,0xFF,0xE7,0xE7,0xE7,0,0,0,0,0,0},
    // 'B'
    {0xFC,0xFE,0xE7,0xE7,0xFE,0xFC,0xFE,0xE7,0xE7,0xFE,0xFC,0,0,0,0,0},
    // 'C'
    {0x3C,0x7E,0xE7,0xE0,0xE0,0xE0,0xE0,0xE7,0x7E,0x3C,0,0,0,0,0,0},
    // 'D'
    {0xFC,0xFE,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xFE,0xFC,0,0,0,0,0,0},
    // 'E'
    {0xFF,0xFF,0xE0,0xE0,0xFE,0xFE,0xE0,0xE0,0xFF,0xFF,0,0,0,0,0,0},
    // 'F'
    {0xFF,0xFF,0xE0,0xE0,0xFE,0xFE,0xE0,0xE0,0xE0,0xE0,0,0,0,0,0,0},
    // 'G'
    {0x3C,0x7E,0xE7,0xE0,0xE0,0xEF,0xEF,0xE7,0x7E,0x3C,0,0,0,0,0,0},
    // 'H'
    {0xE7,0xE7,0xE7,0xE7,0xFF,0xFF,0xE7,0xE7,0xE7,0xE7,0,0,0,0,0,0},
    // 'I'
    {0x7E,0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x7E,0,0,0,0,0,0},
    // 'J'
    {0x1F,0x1F,0x07,0x07,0x07,0x07,0xE7,0xE7,0x7E,0x3C,0,0,0,0,0,0},
    // 'K'
    {0xE7,0xEE,0xFC,0xF8,0xF0,0xF8,0xFC,0xEE,0xE7,0xE7,0,0,0,0,0,0},
    // 'L'
    {0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xFF,0xFF,0,0,0,0,0,0},
    // 'M'
    {0xC3,0xE7,0xFF,0xFF,0xDB,0xC3,0xC3,0xC3,0xC3,0xC3,0,0,0,0,0,0},
    // 'N'
    {0xE7,0xF7,0xFF,0xFF,0xFF,0xEF,0xE7,0xE7,0xE7,0xE7,0,0,0,0,0,0},
    // 'O'
    {0x3C,0x7E,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0x7E,0x3C,0,0,0,0,0,0},
    // 'P'
    {0xFC,0xFE,0xE7,0xE7,0xFE,0xFC,0xE0,0xE0,0xE0,0xE0,0,0,0,0,0,0},
    // 'Q'
    {0x3C,0x7E,0xE7,0xE7,0xE7,0xE7,0xE7,0xF7,0x7E,0x3D,0,0,0,0,0,0},
    // 'R'
    {0xFC,0xFE,0xE7,0xE7,0xFE,0xFC,0xF8,0xFC,0xEE,0xE7,0,0,0,0,0,0},
    // 'S'
    {0x3C,0x7E,0xE7,0xE0,0x7E,0x3C,0x07,0xE7,0x7E,0x3C,0,0,0,0,0,0},
    // 'T'
    {0xFF,0xFF,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0,0,0,0,0,0},
    // 'U'
    {0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0x7E,0x3C,0,0,0,0,0,0},
    // 'V'
    {0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0x7E,0x3C,0x18,0,0,0,0,0,0},
    // 'W'
    {0xC3,0xC3,0xC3,0xC3,0xC3,0xDB,0xFF,0xFF,0xE7,0xC3,0,0,0,0,0,0},
    // 'X'
    {0xE7,0xE7,0x7E,0x3C,0x18,0x3C,0x7E,0xE7,0xE7,0xE7,0,0,0,0,0,0},
    // 'Y'
    {0xE7,0xE7,0x7E,0x3C,0x18,0x18,0x18,0x18,0x18,0x18,0,0,0,0,0,0},
    // 'Z'
    {0xFF,0xFF,0x07,0x0E,0x1C,0x38,0x70,0xE0,0xFF,0xFF,0,0,0,0,0,0},
};

// Map ASCII to font8x16 index
static int fontIndex(char c) {
    if (c == ' ') return 0;
    if (c == '-') return 1;
    if (c == ':') return 2;
    if (c >= '0' && c <= '9') return 3 + (c - '0');
    if (c >= 'A' && c <= 'Z') return 13 + (c - 'A');
    return 0; // fallback to space
}

void Panel::drawChar(uint16_t posX, uint16_t posY, char c, uint8_t r, uint8_t g, uint8_t b) {
    int idx = fontIndex(c);
    for (uint8_t row = 0; row < 16; ++row) {
        uint8_t bits = font8x16[idx][row];
        for (uint8_t col = 0; col < 8; ++col) {
            if (bits & (1 << (7 - col))) {
                drawPixel(posX + col, posY + row, r, g, b);
            }
        }
    }
}

void Panel::drawText(uint16_t posX, uint16_t posY, const char* text, uint8_t r, uint8_t g, uint8_t b) {
    uint16_t x = posX;
    for (size_t i = 0; text[i] != '\0'; ++i) {
        drawChar(x, posY, text[i], r, g, b);
        x += 8; // 8 pixels per char
    }
}
#include "hub75e.h"
#include <Arduino.h>

// gamma 查表（与你给出的保持一致）
static const uint16_t Translate8To16Bit[256] = {
    0,     46,    92,    139,   186,   233,   280,   327,   375,   422,   470,   519,   567,   615,   664,   713,   762,   812,   861,   911,   961,   1011,
    1061,  1112,  1163,  1214,  1265,  1317,  1368,  1420,  1473,  1525,  1578,  1631,  1684,  1737,  1791,  1844,  1899,  1953,  2007,  2062,  2117,  2173,
    2228,  2284,  2340,  2397,  2453,  2510,  2568,  2625,  2683,  2741,  2799,  2858,  2917,  2976,  3036,  3096,  3156,  3216,  3277,  3338,  3399,  3461,
    3523,  3586,  3648,  3711,  3775,  3838,  3902,  3967,  4032,  4097,  4162,  4228,  4294,  4361,  4428,  4495,  4563,  4631,  4699,  4768,  4838,  4907,
    4978,  5048,  5119,  5191,  5262,  5335,  5407,  5481,  5554,  5628,  5703,  5778,  5853,  5929,  6006,  6083,  6160,  6238,  6317,  6396,  6476,  6556,
    6636,  6718,  6799,  6882,  6965,  7048,  7132,  7217,  7302,  7388,  7475,  7562,  7650,  7739,  7828,  7918,  8008,  8099,  8191,  8284,  8377,  8472,
    8567,  8662,  8759,  8856,  8954,  9053,  9153,  9253,  9355,  9457,  9560,  9664,  9769,  9875,  9982,  10090, 10199, 10309, 10420, 10532, 10645, 10760,
    10875, 10991, 11109, 11228, 11348, 11469, 11591, 11715, 11840, 11967, 12094, 12223, 12354, 12486, 12620, 12755, 12891, 13030, 13169, 13311, 13454, 13599,
    13746, 13895, 14045, 14198, 14352, 14509, 14667, 14828, 14991, 15157, 15324, 15494, 15667, 15842, 16020, 16200, 16383, 16569, 16758, 16951, 17146, 17345,
    17547, 17752, 17961, 18174, 18391, 18612, 18837, 19067, 19301, 19539, 19783, 20032, 20286, 20546, 20812, 21083, 21361, 21646, 21938, 22237, 22544, 22859,
    23183, 23516, 23859, 24211, 24575, 24950, 25338, 25739, 26153, 26583, 27029, 27493, 27975, 28478, 29003, 29553, 30130, 30736, 31375, 32051, 32767, 33530,
    34345, 35221, 36167, 37195, 38322, 39567, 40959, 42537, 44359, 46514, 49151, 52551, 57343, 65535};

Panel::Panel(uint16_t width, uint16_t height, uint16_t scan_lines, const uint8_t* pins)
    // 构造函数：初始化 Panel 对象，设置显示参数和引脚编号。
    // 参数：
    //   width       —— 显示屏宽度（像素）
    //   height      —— 显示屏高度（像素）
    //   scan_lines  —— 扫描线数（通常为面板高度的一半）
    //   pins        —— 指向包含14个引脚编号的数组，依次为：CLK, LE, OE, A, B, C, D, E, R1, G1, B1, R2, G2, B2
    : width(width),
      height(height),
      scanLines(scan_lines),
      _PIN_CLK(pins[0]),  // 时钟引脚
      _PIN_LE(pins[1]),   // 锁存引脚
      _PIN_OE(pins[2]),   // 输出使能引脚
      _PIN_A(pins[3]),    // 行选地址A
      _PIN_B(pins[4]),    // 行选地址B
      _PIN_C(pins[5]),    // 行选地址C
      _PIN_D(pins[6]),    // 行选地址D
      _PIN_E(pins[7]),    // 行选地址E
      _PIN_R1(pins[8]),   // 上半屏红色
      _PIN_G1(pins[9]),   // 上半屏绿色
      _PIN_B1(pins[10]),  // 上半屏蓝色
      _PIN_R2(pins[11]),  // 下半屏红色
      _PIN_G2(pins[12]),  // 下半屏绿色
      _PIN_B2(pins[13])   // 下半屏蓝色
{
    std::copy(pins, pins + 14, _gpioPins);  // 保存引脚数组指针，拷贝到成员数组，避免悬空指针
    // initialize members that cannot be computed at header time
    _displayNumberChips = width / 16;
    _screenBuffer[0] = nullptr;
    _screenBuffer[1] = nullptr;
    _displayBufferLineSize = width * 16;
    _displayBufferSize = scan_lines * _displayBufferLineSize;
    _canvas = nullptr;
    _activeScreenBuffer = 0;
    _fps = 30;
}

void Panel::begin() {
    // 分配双缓冲并清零
    _screenBuffer[0] = (uint8_t*)malloc(_displayBufferSize);  // 分配第一个屏幕缓冲区内存
    _screenBuffer[1] = (uint8_t*)malloc(_displayBufferSize);  // 分配第二个屏幕缓冲区内存（用于双缓冲）
    _canvas = (uint8_t*)malloc(width * height * 3);           // 分配画布内存，每个像素3字节（RGB）

    if (!_screenBuffer[0] || !_screenBuffer[1] || !_canvas) {  // 检查内存分配是否成功
        Serial.println("Failed to allocate screen buffers!");  // allocation failed -> print error
        while (1)
            delay(1000);  // 程序停在此处，防止后续访问空指针导致崩溃
    }
    memset(_screenBuffer[0], 0, _displayBufferSize);  // 将第一个屏幕缓冲区清零，确保初始内容全为黑色
    memset(_screenBuffer[1], 0, _displayBufferSize);  // 将第二个屏幕缓冲区清零，保证双缓冲一致
    memset(_canvas, 0, width * height * 3);           // 将画布清零，所有像素初始为黑色（R/G/B=0）

    // 初始化引脚
    for (uint8_t pin = 0; pin < 14; pin++) {
        pinMode(_gpioPins[pin], OUTPUT);    // 将每个功能引脚设置为输出模式
        digitalWrite(_gpioPins[pin], LOW);  // 初始化所有引脚为低电平，防止上电时误动作或闪烁
    }

    _setCfgValue();  // 配置寄存器

    // 因为 xTaskCreatePinnedToCore 需要一个 void* 类型的参数，所以我们只能传入一个静态成员函数 (_dataTask)。
    // 不能传入非静态成员函数。因为非静态成员函数本身会隐式带有一个this指针，不符合xTaskCreatePinnedToCore对参数的要求（ void* 类型）。
    // 传入的静态函数本身不带有this指针。为了在任务函数中访问 Panel 类的成员变量和方法，我们需要将当前对象的地址(this)传递给任务函数。
    // 这个地址就是begin() 里的 this ，也就是你建的这个 panel 对象的地址。这样任务函数就可以通过这个指针访问 Panel 类的成员变量和方法。
    xTaskCreatePinnedToCore(_dataTask, "_dataTask", 4096, this, 1, NULL, 1);        // 创建数据处理任务，绑定到核心1，参数为当前Panel对象指针
    xTaskCreatePinnedToCore(_refreshTask, "_refreshTask", 2048, this, 1, NULL, 0);  // 创建刷新显示任务，绑定到核心0，参数同样为当前Panel对象指针
}

void Panel::_dataTask(void* pvParameters) {
    // 显式标记参数未被使用，用于消除编译器关于“未使用参数”的警告。在 C / C++     // 中，如果一个函数参数没有被用到，编译器通常会发出警告。
    // 加上(void) pvParameters; 就是告诉编译器：“我知道这个参数没用，这是有意为之，不要警告。”
    (void)pvParameters;

    Panel* thisPanel = static_cast<Panel*>(pvParameters);  // 将任务参数强制转换为 Panel*，以便访问当前对象的成员
    uint8_t red, green, blue;                              // 用于暂存每个像素的 RGB 分量
    unsigned long start = micros();                        // 记录当前时间，用于后续帧率控制
    unsigned long time;                                    // 用于计算每帧处理耗时
    uint8_t delayTime = 5;                                 // 延时5毫秒
    while (1) {
        unsigned char in_activeScreenBuffer = 1 - thisPanel->_activeScreenBuffer;         // 计算当前不活跃的屏幕缓冲区索引（0或1）
        unsigned char* inactiveBuffer = thisPanel->_screenBuffer[in_activeScreenBuffer];  // 获取不活跃屏幕缓冲区的指针
        // 获取活跃屏幕缓冲区的指针，用于后续刷新显示，刷屏成员函数会使用它
        unsigned char* activeBuffer = thisPanel->_screenBuffer[thisPanel->_activeScreenBuffer];

        // 遍历显示屏上的每个像素
        for (unsigned int x = 0; x < thisPanel->width; x++) {       // 遍历每列（x坐标）
            for (unsigned int y = 0; y < thisPanel->height; y++) {  // 遍历每行（y坐标）
                // 从画布中获取当前像素的红色、绿色和蓝色分量
                red = thisPanel->_canvas[y * thisPanel->width * 3 + x * 3 + 0];    // 获取画布中指定像素的红色值
                green = thisPanel->_canvas[y * thisPanel->width * 3 + x * 3 + 1];  // 获取画布中指定像素的绿色值
                blue = thisPanel->_canvas[y * thisPanel->width * 3 + x * 3 + 2];   // 获取画布中指定像素的蓝色值

                // 将获取的像素颜色值写入不活跃的缓冲区（用来显示的像素数据）
                thisPanel->_cacheWrite(inactiveBuffer, x, y, red, green, blue);  // 写入缓冲区，用于后续显示
            }
        }

        // 将当前不活跃屏幕缓冲区的索引赋值给活跃屏幕缓冲区的索引
        thisPanel->_activeScreenBuffer = in_activeScreenBuffer;  // 切换活跃的缓冲区，准备进行下次显示

        vTaskDelay(delayTime / portTICK_PERIOD_MS);  // 延时5毫秒，给其他任务一些执行时间，防止 CPU 占用过高
        // 帧率thisPanel->_fps=60，相当于每帧用时16,666微秒。time=11666微秒-delayTime*1000微秒-（micros()-start）微秒
        time = 1000000 / thisPanel->_fps - delayTime * 1000 - (micros() - start);
        if (time < (1000000 / thisPanel->_fps - delayTime * 1000)) delayMicroseconds(time);  // 如果剩余时间小于16666微秒，则进行微秒级延时
        start = micros();                                                                    // 更新start时间，准备计算下一帧的耗时
    }
}

/**
 * @brief 设置面板配置值
 * 该函数用于向面板发送一系列配置命令，包括VSYNC同步信号、预激活信号、
 * 使能所有输出以及多个寄存器的配置值
 */
void Panel::_setCfgValue() {
    _sendLatch(3);   // VSYNC
    _sendLatch(14);  // Pre-active
    _sendLatch(12);  // Enable all output
    _sendLatch(14);
    _sendConfiguration(_cfgValue[0], 4);  // Reg1
    _sendLatch(14);
    _sendConfiguration(_cfgValue[1], 6);  // Reg2
    _sendLatch(14);
    _sendConfiguration(_cfgValue[2], 8);  // Reg3
    _sendLatch(14);
    _sendConfiguration(_cfgValue[3], 10);  // Reg4
    _sendLatch(14);
    _sendConfiguration(_cfgValue[4], 2);  // Debug
}

void IRAM_ATTR Panel::_sendLatch(uint8_t clocks) {
    _digitalWriteFast(_PIN_LE, 1);
    while (clocks--)
        _sendClock();
    _digitalWriteFast(_PIN_LE, 0);
}

void IRAM_ATTR Panel::_digitalWriteFast(uint8_t pinNum, bool value) {
    if (value)
        GPIO.out_w1ts = (1UL << pinNum);
    else
        GPIO.out_w1tc = (1UL << pinNum);
}

void IRAM_ATTR Panel::_sendClock() {
    _digitalWriteFast(_PIN_CLK, 1);
    _digitalWriteFast(_PIN_CLK, 0);
}

void Panel::_sendConfiguration(uint16_t data, uint8_t latches) {
    uint8_t num = _displayNumberChips;
    uint16_t dataMask;
    const uint32_t zero = 0x00000000;
    const uint32_t rgbrgbMask = 0x000FC000;  // 对应于GPIO14~19脚

    latches = 16 - latches;  // 硬件协议要求
    while (num--) {
        for (uint8_t x = 0; x < 16; x++) {
            dataMask = (uint16_t)(0x8000U >> x);
            _digitalWriteEvenFaster((data & dataMask) ? rgbrgbMask : zero, rgbrgbMask);
            if (num == 0 && x == latches) _digitalWriteFast(_PIN_LE, 1);
            _sendClock();
        }
        _digitalWriteFast(_PIN_LE, 0);
    }
}

void IRAM_ATTR Panel::_digitalWriteEvenFaster(uint32_t data, uint32_t mask) {
    GPIO.out = (GPIO.out & ~mask) | data;
}

void IRAM_ATTR Panel::_sendScanLine(unsigned char line) {
    unsigned long scanLine = 0x00000000;

    if (line & 0x1) scanLine += 1;
    if (line >> 1 & 0x1) scanLine += 2;
    if (line >> 2 & 0x1) scanLine += 4;
    if (line >> 3 & 0x1) scanLine += 16;
    if (line >> 4 & 0x1) scanLine += 32;

    _digitalWriteEvenFaster(scanLine << 21, 0x06E00000);
}

void IRAM_ATTR Panel::_sendPwmClock(unsigned char clocks) {
    while (clocks--) {
        _digitalWriteFast(_PIN_OE, 1);
        _digitalWriteFast(_PIN_OE, 0);
    }
}

void Panel::_refreshTask(void* pvParameters) {
    Panel* instance = static_cast<Panel*>(pvParameters);  // 将任务参数转换为 Panel* 类型，以便访问成员函数和变量

    while (1) {
        instance->_refreshFrame();  // 调用 Panel 类的 _refreshFrame 方法来刷新显示内容
        vTaskDelay(1);              // 延时1毫秒，给其他任务一些执行时间，防止 CPU 占用过高
    }
}

void Panel::_refreshFrame() {
    unsigned char* activeBuffer = _screenBuffer[_activeScreenBuffer];  // 获取当前活跃的屏幕缓冲区指针
    unsigned int pos;                                                  // 用于计算数据在缓冲区中的位置
    unsigned int bufferPos;                                            // 用于计算当前扫描行在缓冲区中的起始位置

    _sendLatch(3);  // 发送 VSYNC 命令

    // since the generation of the output signal in the ICN2053 chips is directly tied to the input clock signal when receiving pixel data,
    // the order and amount of clock cycles, latches, PWM clock and so on can not be changed.
    for (unsigned int y = 0; y < scanLines; y++) {     // 遍历每一行扫描线。每扫描一次，上下半屏会同时扫描。因此只要扫描半屏
        for (unsigned int x = 0; x < 16; x++) {        // 遍历ICND2153的引脚OUT[15:0]
            bufferPos = y * width * 16 + x * 16;       // 计算当前扫描行在缓冲区中的起始位置
            _sendScanLine(y % 2 * scanLines / 2 + x);  // 发送当前扫描行的地址信息，y % 2 用于处理奇偶行的扫描顺序
            _sendPwmClock(138);                        // 发送 PWM 时钟信号，138个时钟周期用于 ICND2153 芯片的显示，必须138个脉冲，不能多也不能少

            for (unsigned int sect = 0; sect < _displayNumberChips; sect++) {  // 遍历每个芯片（每个芯片处理16列数据）
                pos = bufferPos + sect * 16 * 16;                              // 计算当前芯片在缓冲区中的位置，注意pos的值是跳跃的
                // Serial.printf("x=%u, y=%u, bufferPos=%u, pos=%u\n", x, y, bufferPos, pos);   // 分配失败则输出错误信息
                // delay(100);
                // 将16位灰度值写入ICND2153。左移14位是因为6片ICND2153的输入引脚(R1,G1,B2,R2,G2,B2)从第14位开始，因此主程序里的引脚号是固定的。
                for (unsigned char bit = 0; bit < 16; bit++) {
                    // 0x000FC000 是6个RGB通道的掩码，确保只修改R1, G1, B1, R2, G2, B2通道的值
                    _digitalWriteEvenFaster(activeBuffer[pos + bit] << 14, 0x000FC000);
                    if (sect == _displayNumberChips - 1 && bit == 15) _digitalWriteFast(_PIN_LE, 1);  // 最后一个芯片的最后一个位时，发送锁存信号
                    _sendClock();  // 每发送一位，发送时钟信号，驱动 ICND2153 芯片将数据移入寄存器
                }
                // Serial.println();
                _digitalWriteFast(_PIN_LE, 0);  // 发送锁存信号，锁存当前芯片的数据
            }
        }
    }
}

void Panel::_cacheWrite(unsigned char* buffer, unsigned int posX, unsigned int posY, unsigned char red, unsigned char green, unsigned char blue) {
    unsigned long bufferPos = posX * 16 + posY * _displayBufferLineSize;
    unsigned int inputMask = 0x8000;
    unsigned char outputmask = 0x07;
    unsigned char rgb = 0x00;
    unsigned int red16Bit = Translate8To16Bit[red];
    unsigned int green16Bit = Translate8To16Bit[green];
    unsigned int blue16Bit = Translate8To16Bit[blue];
    // Serial.printf("posX=%u, posY=%u, bufferPos=%u\n", posX, posY, bufferPos);
    // delay(25);

    if (posY > scanLines - 1) {
        outputmask = 0x38;
        bufferPos -= scanLines * _displayBufferLineSize;
    }

    // data is saved in a format where the refresh method just needs to send it out instead of formatting it first, which makes it 7 _fps faster
    // the highest bit of each color is saved in a char and written into the buffer, then the second highest bit and so on
    // since the RGB values of two different lines are sent at the same time, when y is bigger than the scan line area the data is written into different bits
    // of the buffer data
    for (unsigned char x = 0; x < 16; x++) {
        rgb = 0x00;
        inputMask = 0x8000 >> x;

        if (red16Bit & inputMask) rgb += 1;
        if (green16Bit & inputMask) rgb += 2;
        if (blue16Bit & inputMask) rgb += 4;

        if (posY > scanLines - 1) rgb <<= 3;

        // write data without changing the surrounding bits
        buffer[bufferPos + x] = (buffer[bufferPos + x] & ~outputmask) | rgb;
        // Serial.printf("bufferPos=%u, bufferPos + %u=%u\n", bufferPos, x, bufferPos + x);
    }
}

void Panel::clearScreen() {
    // Clear the _canvas buffer to black (RGB: 0, 0, 0)
    memset(_canvas, 0, width * height * 3);
}

void Panel::setBackgound(uint8_t red8BitGray, uint8_t green8BitGray, uint8_t blue8BitGray){
    // Correct iteration order: y over height, x over width
    for (uint16_t y = 0; y < height; y++) {
        for (uint16_t x = 0; x < width; x++) {
            drawPixel(x, y, red8BitGray, green8BitGray, blue8BitGray);
        }
    }
}
void Panel::drawPixel(uint16_t posX, uint16_t posY, uint8_t red8BitGray, uint8_t green8BitGray, uint8_t blue8BitGray) {
    if (posX >= width || posY >= height) {
        Serial.printf("Invalid pixel coordinates: (%u, %u)\n", posX, posY);
        return;
    }
    // Serial.printf("width=%u, height=%u\n", width, height);
    // Serial.printf("posX=%u, posY=%u\n", posX, posY);

    uint16_t basePos = posY * width * 3 + posX * 3;
    // Serial.printf("basePos=%u\n", basePos);

    _canvas[basePos] = red8BitGray;
    _canvas[basePos + 1] = green8BitGray;
    _canvas[basePos + 2] = blue8BitGray;
}

void Panel::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t red8BitGray, uint8_t green8BitGray, uint8_t blue8BitGray) {
    // Bounds checking to ensure coordinates are within the 64x64 panel
    if (x0 >= width || y0 >= height || x1 >= width || y1 >= height) {
        Serial.printf("Invalid line coordinates: (%u,%u) to (%u,%u)\n", x0, y0, x1, y1);
        return;
    }

    // Bresenham's line algorithm
    int16_t dx = abs((int16_t)x1 - (int16_t)x0);  // Absolute difference in x
    int16_t dy = abs((int16_t)y1 - (int16_t)y0);  // Absolute difference in y
    int16_t sx = x0 < x1 ? 1 : -1;                // Step direction for x
    int16_t sy = y0 < y1 ? 1 : -1;                // Step direction for y
    int16_t err = (dx > dy ? dx : -dy) / 2;       // Error term for decision
    int16_t x = x0;
    int16_t y = y0;

    while (true) {
        // Draw the current pixel
        drawPixel(x, y, red8BitGray, green8BitGray, blue8BitGray);
        // Check if we've reached the end point
        if (x == x1 && y == y1) break;
        int16_t e2 = err;
        // Update x if error term allows
        if (e2 > -dx) {
            err -= dy;
            x += sx;
        }
        // Update y if error term allows
        if (e2 < dy) {
            err += dx;
            y += sy;
        }
    }
}

void Panel::drawRect(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t red8BitGray, uint8_t green8BitGray, uint8_t blue8BitGray) {
    // Bounds checking to ensure coordinates are within the 64x64 panel
    if (x0 >= width || y0 >= height || x1 >= width || y1 >= height) {
        Serial.printf("Invalid rectangle coordinates: (%u,%u) to (%u,%u)\n", x0, y0, x1, y1);
        return;
    }

    // Draw four sides of the rectangle using drawLine
    drawLine(x0, y0, x1, y0, red8BitGray, green8BitGray, blue8BitGray);  // Top
    drawLine(x0, y1, x1, y1, red8BitGray, green8BitGray, blue8BitGray);  // Bottom
    drawLine(x0, y0, x0, y1, red8BitGray, green8BitGray, blue8BitGray);  // Left
    drawLine(x1, y0, x1, y1, red8BitGray, green8BitGray, blue8BitGray);  // Right
}

void Panel::fillRect(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t red8BitGray, uint8_t green8BitGray, uint8_t blue8BitGray) {
    // Bounds checking to ensure coordinates are within the 64x64 panel
    if (x0 >= width || y0 >= height || x1 >= width || y1 >= height) {
        Serial.printf("Invalid fill rectangle coordinates: (%u,%u) to (%u,%u)\n", x0, y0, x1, y1);
        return;
    }

    // Normalize coordinates to ensure x0 <= x1 and y0 <= y1
    uint16_t xMin = min(x0, x1);
    uint16_t xMax = max(x0, x1);
    uint16_t yMin = min(y0, y1);
    uint16_t yMax = max(y0, y1);

    // Iterate over the rectangle area and draw each pixel
    for (uint16_t y = yMin; y <= yMax; y++) {
        for (uint16_t x = xMin; x <= xMax; x++) {
            drawPixel(x, y, red8BitGray, green8BitGray, blue8BitGray);
        }
    }
}

void Panel::drawCircle(uint16_t x0, uint16_t y0, uint16_t radius, uint8_t red8BitGray, uint8_t green8BitGray, uint8_t blue8BitGray) {
    // Bounds checking for the circle's bounding box
    if (x0 < radius || x0 >= width - radius || y0 < radius || y0 >= height - radius || radius == 0) {
        Serial.printf("Invalid circle parameters: center (%u,%u), radius %u\n", x0, y0, radius);
        return;
    }

    // Bresenham's circle algorithm
    int16_t x = radius;
    int16_t y = 0;
    int16_t err = 0;

    while (x >= y) {
        // Draw pixels in all eight octants
        drawPixel(x0 + x, y0 + y, red8BitGray, green8BitGray, blue8BitGray);
        drawPixel(x0 + y, y0 + x, red8BitGray, green8BitGray, blue8BitGray);
        drawPixel(x0 - y, y0 + x, red8BitGray, green8BitGray, blue8BitGray);
        drawPixel(x0 - x, y0 + y, red8BitGray, green8BitGray, blue8BitGray);
        drawPixel(x0 - x, y0 - y, red8BitGray, green8BitGray, blue8BitGray);
        drawPixel(x0 - y, y0 - x, red8BitGray, green8BitGray, blue8BitGray);
        drawPixel(x0 + y, y0 - x, red8BitGray, green8BitGray, blue8BitGray);
        drawPixel(x0 + x, y0 - y, red8BitGray, green8BitGray, blue8BitGray);

        if (err <= 0) {
            y += 1;
            err += 2 * y + 1;
        }
        if (err > 0) {
            x -= 1;
            err -= 2 * x + 1;
        }
    }
}

void Panel::fillCircle(uint16_t x0, uint16_t y0, uint16_t radius, uint8_t red8BitGray, uint8_t green8BitGray, uint8_t blue8BitGray) {
    // Bounds checking for the circle's bounding box
    if (x0 < radius || x0 >= width - radius || y0 < radius || y0 >= height - radius || radius == 0) {
        Serial.printf("Invalid fill circle parameters: center (%u,%u), radius %u\n", x0, y0, radius);
        return;
    }

    // Modified Bresenham's circle algorithm to fill the circle
    int16_t x = radius;
    int16_t y = 0;
    int16_t err = 0;

    while (x >= y) {
        // Draw horizontal lines to fill the circle at each y-coordinate
        drawLine(x0 - x, y0 + y, x0 + x, y0 + y, red8BitGray, green8BitGray, blue8BitGray);
        drawLine(x0 - y, y0 + x, x0 + y, y0 + x, red8BitGray, green8BitGray, blue8BitGray);
        drawLine(x0 - x, y0 - y, x0 + x, y0 - y, red8BitGray, green8BitGray, blue8BitGray);
        drawLine(x0 - y, y0 - x, x0 + y, y0 - x, red8BitGray, green8BitGray, blue8BitGray);

        if (err <= 0) {
            y += 1;
            err += 2 * y + 1;
        }
        if (err > 0) {
            x -= 1;
            err -= 2 * x + 1;
        }
    }
}

// 显示一个位图，可以是ASCII码、汉字或者任意位图
void Panel::diaplayBitmap(uint16_t posX, uint16_t posY, uint16_t bitmapWidth, uint16_t bitmapHeight, const uint8_t* bitmapData, const uint8_t* grayScale) {
    // 参数检查
    if (posX >= width || posY >= height) {
        Serial.println("Display position out of range");
        return;
    }

    if (bitmapWidth == 0 || bitmapHeight == 0) {
        Serial.println("Invalid bitmap dimensions");
        return;
    }

    if (bitmapData == nullptr) {
        Serial.println("Bitmap data is null");
        return;
    }

    // 计算每行需要的字节数 (每像素1位，所以每行需要 ceil(bitmapWidth/8) 字节)
    uint16_t bytesPerRow = (bitmapWidth + 7) / 8;

    // 遍历汉字点阵的每个像素
    for (uint16_t y = 0; y < bitmapHeight; y++) {
        // 确保不会超出显示范围
        if (posY + y >= height) break;

        for (uint16_t x = 0; x < bitmapWidth; x++) {
            // 确保不会超出显示范围
            if (posX + x >= width) break;

            // 计算当前像素在字模数据中的位置
            uint16_t byteIndex = y * bytesPerRow + (x / 8);
            uint8_t bitIndex = 7 - (x % 8);  // 位索引 (MSB优先)

            // 检查当前像素是否应该点亮
            if (bitmapData[byteIndex] & (1 << bitIndex)) {
                // 计算在画布中的位置
                uint32_t _canvasIndex = ((posY + y) * width + (posX + x)) * 3;

                // 设置像素颜色
                if (grayScale != nullptr) {
                    // 使用提供的灰度值
                    _canvas[_canvasIndex] = grayScale[0];      // 红色
                    _canvas[_canvasIndex + 1] = grayScale[1];  // 绿色
                    _canvas[_canvasIndex + 2] = grayScale[2];  // 蓝色
                } else {
                    // 默认使用白色
                    _canvas[_canvasIndex] = 0xFF;      // 红色
                    _canvas[_canvasIndex + 1] = 0xFF;  // 绿色
                    _canvas[_canvasIndex + 2] = 0xFF;  // 蓝色
                }
            }
        }
    }
}

